package com.zuo_book.security;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Component;

import javax.crypto.SecretKey;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * JWT工具类 - 整合了JwtService和JwtTokenUtil的功能
 */
@Slf4j
@Component
@RequiredArgsConstructor
public class JwtUtils {

    private final SecretKey jwtSigningKey;

    @Value("${jwt.expiration:86400000}") // 默认24小时
    private Long expiration;

    /**
     * 生成JWT令牌
     * @param userDetails 用户详情
     * @return JWT令牌
     */
    public String generateToken(UserDetails userDetails) {
        Map<String, Object> claims = new HashMap<>();
        
        // 记录密钥和算法信息（不输出实际密钥内容）
        log.info("生成JWT令牌，使用算法: {}, 密钥类型: {}", 
                SignatureAlgorithm.HS512.getJcaName(), 
                jwtSigningKey.getAlgorithm());
        
        // 适当地记录密钥长度，确认是否符合要求
        log.info("JWT密钥长度: {} 位", jwtSigningKey.getEncoded().length * 8);
        
        return Jwts.builder()
                .setClaims(claims)
                .setSubject(userDetails.getUsername())
                .setIssuedAt(new Date(System.currentTimeMillis()))
                .setExpiration(new Date(System.currentTimeMillis() + expiration))
                .signWith(jwtSigningKey, SignatureAlgorithm.HS512)
                .compact();
    }

    /**
     * 从JWT令牌中提取用户名
     * @param token JWT令牌
     * @return 用户名
     */
    public String getUsernameFromToken(String token) {
        try {
            Claims claims = getAllClaimsFromToken(token);
            if (claims != null) {
                return claims.getSubject();
            }
            return null;
        } catch (Exception e) {
            log.error("从JWT令牌中提取用户名时发生错误: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 从JWT令牌中提取到期时间
     * @param token JWT令牌
     * @return 到期时间
     */
    public Date getExpirationDateFromToken(String token) {
        return getClaimFromToken(token, Claims::getExpiration);
    }

    /**
     * 从JWT令牌中提取指定的声明
     * @param token JWT令牌
     * @param claimsResolver 声明解析器
     * @return 声明值
     */
    public <T> T getClaimFromToken(String token, Function<Claims, T> claimsResolver) {
        final Claims claims = getAllClaimsFromToken(token);
        return claimsResolver.apply(claims);
    }

    /**
     * 从JWT令牌中提取所有声明
     * @param token JWT令牌
     * @return 所有声明
     */
    private Claims getAllClaimsFromToken(String token) {
        try {
            return Jwts.parserBuilder()
                    .setSigningKey(jwtSigningKey)
                    .build()
                    .parseClaimsJws(token)
                    .getBody();
        } catch (Exception e) {
            log.error("无法解析JWT令牌: {}", e.getMessage());
            return null;
        }
    }

    /**
     * 检查JWT令牌是否已过期
     * @param token JWT令牌
     * @return 是否已过期
     */
    private Boolean isTokenExpired(String token) {
        try {
            final Date expiration = getExpirationDateFromToken(token);
            return expiration.before(new Date());
        } catch (Exception e) {
            log.error("检查令牌过期时发生错误: {}", e.getMessage());
            return true;
        }
    }

    /**
     * 验证JWT令牌
     * @param token JWT令牌
     * @return 是否有效
     */
    public Boolean validateToken(String token) {
        try {
            // 首先检查令牌是否为null或空
            if (token == null || token.isEmpty()) {
                log.warn("收到空的JWT令牌");
                return false;
            }

            // 基本格式验证 - JWT应该有两个点号分隔三部分
            if (!token.contains(".") || token.split("\\.").length != 3) {
                log.warn("JWT令牌格式不正确，应该包含两个点号分隔三部分: {}", 
                         token.length() > 10 ? token.substring(0, 10) + "..." : token);
                return false;
            }

            // 检查令牌是否过期
            if (isTokenExpired(token)) {
                log.warn("JWT令牌已过期");
                return false;
            }

            // 检查能否正确解析令牌
            Claims claims = getAllClaimsFromToken(token);
            if (claims == null || claims.getSubject() == null) {
                log.warn("无法从JWT令牌中提取有效声明");
                return false;
            }
            
            log.debug("JWT令牌验证成功，用户: {}", claims.getSubject());
            return true;
        } catch (Exception e) {
            log.error("验证令牌时发生错误: {}", e.getMessage());
            return false;
        }
    }

    /**
     * 验证JWT令牌对特定用户是否有效
     * @param token JWT令牌
     * @param userDetails 用户详情
     * @return 是否有效
     */
    public boolean validateToken(String token, UserDetails userDetails) {
        final String username = getUsernameFromToken(token);
        return (username.equals(userDetails.getUsername()) && !isTokenExpired(token));
    }
} 